package xj.graph2d.app.project;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import xj.app.Application;
import xj.graph2d.Document;
import xj.graph2d.DocumentException;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.SimpleDrawDocument;
import xj.graph2d.app.DrawApp;
import xj.lang.CodeWriter;
import xj.util.StringUtils;

public class ProjectItem {

  public ProjectItem() {
  }

  public ProjectItem(String name) {
    this.name = name;
  }

  public ProjectItem(SimpleDrawDocument document) {
    this(document, false);
  }

  public ProjectItem(SimpleDrawDocument document, boolean setpath) {
    if (document != null) {
      this.document = document;
      this.name = document.getName();
      this.type = document.getDocumentType();
      if (setpath) {
	String ext = "." + Document.getDefaultExtension(document.getDocumentType());
	String filename = this.name;
	if (!filename.endsWith(ext)) {
	  filename = filename + ext;
	}
	setDocumentPath(filename);
      } else {
	this.documentPath = document.getFilename();
      }
    }
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    setName(name, true);
  }

  public void setName(String name, boolean setpath) {
    this.name = name;
    if (document != null) {
      document.setName(name);
      if (setpath) {
	String ext = "." + Document.getDefaultExtension(document.getDocumentType());
	String filename = name;
	if (!filename.endsWith(ext)) {
	  filename = filename + ext;
	}
	setDocumentPath(filename);
      }
    }
  }

  /**
   * Return the full qualified name, with package names
   * 
   * pkg1:pkg2:name
   * 
   */
  public String getFullName() {
    return getFullName(":", false);
  }

  public String getFullName(String sep) {
    return getFullName(sep, false);
  }

  public String getFullName(String sep, boolean noproj) {
    if (sep == null) { 
      sep = ":";
    }
    if (parent == null || 
	(noproj && parent instanceof Project)) {
      return name;
    } else {
      String pname = parent.getFullName(sep, noproj);
      if (pname != null && name != null) {
	return (pname + sep + name);
      } else {
	return name;
      }
    }
  }

  public void updateName() {
    if (document != null) {
      name = document.getName();
    }
  }

  public Document.DocumentType getItemType() {
    if (document != null) {
      return document.getDocumentType();
    } else {
      return type;
    }
  }

  public void setItemType(Document.DocumentType type) {
    this.type = type;
    if (document != null) {
      document.setDocumentType(type);
    }
  }

  public String getType() { 
    Document.DocumentType type = getItemType();
    if (type != null) {
      return StringUtils.humanize(type.toString());
    }
    return null;
  }

  public String getAuthor() {
    return author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getOrganization() {
    return organization;
  }

  public void setOrganization(String organization) {
    this.organization = organization;
  }

  public ProjectFolder getParent() {
    return parent;
  }

  public void setParent(ProjectFolder parent) {
    this.parent = parent;

    Project project = getOwner();
    if (document != null && project != null) {
      String filename = getDocumentFullPath();
      //String loc = project.getWorkingDir();
      String loc = project.getFullPath();
      filename = Project.getRelativePath(loc, filename);
      document.setFilename(filename);
      this.documentPath = document.getFilename();
    }
  }

  public Project getOwner() {
    ProjectItem cur = this;
    while (cur != null) {
      if (cur instanceof Project) {
	return (Project) cur;
      }
      cur = cur.getParent();
    }
    return null;
  }

  public boolean isChanged() {
    if (document != null) {
      return document.isDocumentChanged();
    }
    return false;
  }

  public boolean isOpen() { 
    return document != null;
  }

  public void openDocument() 
    throws IOException, DocumentException {
    Project project = getOwner(); 
    OpenDocumentListener odl = null;
    if (project != null) { 
      odl = project.getOpenDocumentListener();
    }
    if (odl != null) { 
      odl.openDocument(this);
    } else { 
      loadDocument();
    }
  }

  public SimpleDrawDocument getDocument() {
    return document;
  }

  public void setDocument(SimpleDrawDocument document) {
    this.document = document;
    if (document != null) {
      this.name = document.getName();
      this.type = document.getDocumentType();
    }
  }

  public String getDocumentPath() {
    if (document != null) {
      documentPath = document.getFilename();
    }
    return documentPath;
  }

  private void setDocumentPath(String documentPath) {
    this.documentPath = documentPath;
    if (document != null) {
      document.setFilename(documentPath);
    }
  }

  public File getDocumentFile() {
    String filename = getDocumentFullPath();
    if (filename != null) {
      return new File(filename);
    }
    return null;
  }

  public String getFullPath() {
    String loc = null;
    if (parent != null && !(parent instanceof Project)) {
      loc = parent.getFullPath();
    } else {
      Project project = getOwner();
      if (project != null) {
	//loc = project.getWorkingDir();
	String wd = project.getWorkingDir();
	if (wd != null) { 
	  loc = wd + File.separator + project.getName();
	} else { 
	  loc = project.getName();
	}
      }
    }
    if (loc == null) {
      return name;
    } else {
      return loc + File.separator + name;
    }
  }

  public String getRelativePathToProject() { 
    if (this instanceof Project) {
      return null;
    }
    if (parent == null || parent instanceof Project) {
      return name; 
    }
    String loc = parent.getRelativePathToProject();
    if (loc != null) { 
      return loc + File.separator + name;
    } else { 
      return name; 
    }
  }

  public String getDocumentFullPath() {
    String loc = null;
    if (parent != null && !(parent instanceof Project)) {
      loc = parent.getFullPath();
    } else {
      Project project = getOwner();
      if (project != null) {
	loc = project.getWorkingDir() + File.separator + project.getName();
      }
    }
    String filename = documentPath;
    if (filename != null) {
      File f = new File(filename);
      if (f.isAbsolute()) {
	return filename;
      } else {
	int i = filename.lastIndexOf(File.separator);
	if (i >= 0) {
	  filename = filename.substring(i + 1);
	}
      }
    } else {
      filename = name;
    }
    if (loc != null) {
      filename = loc + File.separator + filename;
    }
    return filename;
  }

  public Map<String, String> getAttributes() { 
    return attr;
  }

  public String getAttribute(String key) { 
    if (key != null) { 
      return attr.get(key); 
    }
    return null;
  }

  public void setAttribute(String key, String value) { 
    if (key != null) { 
      if (value != null) { 
	attr.put(key, value); 
      } else { 
	attr.remove(key);
      }
    }
  }

  public void loadDocument() 
    throws IOException, DocumentException {
    // System.out.println("ProjectItem.loadDocument() @1");

    String filename = getDocumentFullPath();
    if (filename != null) {
      Project project = getOwner();
      if (project != null) {
	Application app = project.getApplication();
	if (app instanceof DrawApp) {
	  DrawApp main = (DrawApp) app;
	  main.loadFile(filename, name, type);
	  document = main.getDoc();
	}
      }
    }
  }

  public void writeXML(CodeWriter out, String namespacePrefix) {
    if (out != null) {
      String itname = Project.getPrefixedName(Project.TAG_NAME_PROJECT_ITEM,
					      namespacePrefix);
      out.println("<" + itname + " name=\"" + name + "\"" + 
		  " type=\"" + getItemType() + "\">");
      out.thisIndentInc();

      String docname = Project.getPrefixedName(Project.TAG_NAME_DOCUMENT,
					       namespacePrefix);
      String path = getDocumentPath();
      Project project = getOwner();
      if (project != null) { 
	String projpath = project.getName() + File.separator;
	if (path.startsWith(projpath)) { 
	  path = path.substring(projpath.length());
	}
      }
      // for cross-platform compatibility
      if (File.separatorChar != '/') {
	path = path.replace(File.separatorChar, '/');
      }
      out.println("<" + docname + " path=\"" + path + "\"/>");
      writeItemInfo(out, namespacePrefix);
      out.thisIndentDec();
      out.println("</" + itname + ">");
    }
  }

  public void loadXML(Element item, String namespaceURI) 
    throws IOException, DocumentException {
    if (item != null) {
      String uri = item.getNamespaceURI();
      if (namespaceURI == null || namespaceURI.equals(uri)) {
	String itname = item.getLocalName();
	if (Project.TAG_NAME_PROJECT_ITEM.equals(itname)) {
	  setName(item.getAttribute("name"));
	  setItemType(Document.toDocumentType(item.getAttribute("type")));
	  if (item.hasChildNodes()) {
	    NodeList nodes = item.getChildNodes();
	    if (nodes != null) {
	      int n = nodes.getLength();
	      for (int i = 0; i < n; i++) {
		Node node = nodes.item(i);
		if (node.getNodeType() == Node.ELEMENT_NODE) {
		  Element e = (Element) node;
		  String name = e.getLocalName();
		  if (Project.TAG_NAME_DOCUMENT.equals(name)) {
		    String docpath = e.getAttribute("path");
		    // for cross-platform compatibility
		    if (File.separatorChar != '/') {
		      docpath = docpath.replace('/', File.separatorChar);
		    }
		    setDocumentPath(docpath);
		    loadDocument();
		  } else {
		    loadItemInfo(node, name);
		  }
		}
	      }
	    }
	  }
	}
      }
    }
  }

  protected void writeItemInfo(CodeWriter out, String namespacePrefix) {
    if (out != null) {
      String nodename;
      if (author != null) {
	nodename = DrawShapeBase.getPrefixedName("Author", namespacePrefix);
	out.println("<" + nodename + ">" + 
		    xj.util.xml.XSLTUtil.filterXMLText(author) + 
		    "</" + nodename + ">");
      }
      if (title != null) {
	nodename = DrawShapeBase.getPrefixedName("Title", namespacePrefix);
	out.println("<" + nodename + ">" + 
		    xj.util.xml.XSLTUtil.filterXMLText(title) + 
		    "</" + nodename + ">");
      }
      if (description != null) {
	nodename = DrawShapeBase.getPrefixedName("Description", namespacePrefix);
	out.println("<" + nodename + ">" + 
		    xj.util.xml.XSLTUtil.filterXMLText(description) + 
		    "</" + nodename + ">");
      }
      if (organization != null) {
	nodename = DrawShapeBase.getPrefixedName("Organization",
						 namespacePrefix);
	out.println("<" + nodename + ">" + 
		    xj.util.xml.XSLTUtil.filterXMLText(organization) + 
		    "</" + nodename + ">");
      }

      if (attr.size() > 0) { 
	nodename = DrawShapeBase.getPrefixedName("Attributes", namespacePrefix);
	out.println("<" + nodename + ">");
	out.thisIndentInc();
	for (Map.Entry<String,String> e : attr.entrySet()) { 
	  String atag = DrawShapeBase.getPrefixedName("Attribute", namespacePrefix);
	  out.println("<" + atag + " key=\"" + e.getKey() + "\" value=\"" + e.getValue() + "\"/>");
	}
	out.thisIndentDec();
	out.println("</" + nodename + ">");
      }
    }
  }

  protected void loadItemInfo(Node node, String name) {
    if (node != null && name != null) {
      Node tnode = node.getFirstChild();
      String text = null;
      if (tnode != null && 
	  (tnode.getNodeType() == org.w3c.dom.Node.TEXT_NODE || 
	   tnode.getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE)) {
	text = tnode.getNodeValue();
      }
      if ("Author".equals(name)) {
	author = text;
      } else if ("Title".equals(name)) {
	title = text;
      } else if ("Description".equals(name)) {
	description = text;
      } else if ("Organization".equals(name)) {
	organization = text;
      } else if ("Attributes".equals(name)) {
	if (node.hasChildNodes()) {
	  NodeList nodes = node.getChildNodes();
	  if (nodes != null) {
	    int n = nodes.getLength();
	    for (int i = 0; i < n; i++) {
	      Node anode = nodes.item(i);
	      if (anode.getNodeType() == Node.ELEMENT_NODE) {
		Element e = (Element) anode;
		String aname = e.getLocalName();
		if ("Attribute".equals(aname)) {
		  String key = e.getAttribute("key");
		  String value = e.getAttribute("value");
		  attr.put(key, value);
		}
	      }
	    }
	  }
	}
      }
    }
  }

  public void saveDocument() throws IOException, DocumentException {
    if (document != null) {
      Project project = getOwner();
      if (project != null) {
	String filename = getDocumentFullPath();
	if (filename != null) {
	  File file = new File(filename);
	  File dir = file.getParentFile();
	  Project.createDir(dir);

	  int filetype = document.getFiletype();
	  document.saveFile(filename, filetype, false);

	  // filename = document.getFilename();
	  document.setName(SimpleDrawDocument.getNameFromFilename(filename));
	  String loc = project.getWorkingDir();
	  filename = Project.getRelativePath(loc, filename);
	  document.setFilename(filename);
	}
      } else {
	document.saveFile();
      }
    }
  }

  public ProjectItem cloneProjectItem() { 
    ProjectItem newitem = new ProjectItem(name);
    newitem.setAuthor(author);
    newitem.setTitle(title);
    newitem.setDescription(description);
    newitem.setOrganization(organization);
    if (document != null) { 
      newitem.setDocument(document.cloneDocument());
    }
    newitem.setName(name);
    return newitem;
  }

  public boolean isDocumentNew() { 
    if (document != null) { 
      return document.isDocumentNew();
    }
    return true; 
  }

  public boolean isDocumentSaved() { 
    if (document != null) { 
      return document.isDocumentSaved();
    }
    return false;
  }

  protected String name; // display name

  protected String author;

  protected String title;

  protected String organization;

  protected String description;

  protected Document.DocumentType type = Document.DocumentType.Generic_Diagram;

  protected Map<String, String> attr = new HashMap<String, String>();

  protected ProjectFolder parent;

  protected SimpleDrawDocument document = null;

  protected String documentPath = null;

}
